﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Vbyte.Extension;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;

namespace Vbyte.DataSource.Utility
{
    /// <summary>
    /// JSON数据解析
    /// </summary>
    public class JSONParse
    {
        /// <summary>
        /// JSON数据解析
        /// </summary>
        /// <param name="json">json字符串</param>
        public JSONParse(string json)
        {
            rawJSON = json;
            JavaScriptSerializer jss = new JavaScriptSerializer();
            object jObj = jss.DeserializeObject(rawJSON);
            jsonDict = jObj as Dictionary<string, object>;
        }

        /// <summary>
        /// JSON数据解析
        /// </summary>
        /// <param name="jsonObject">JSON的.NET数据词典</param>
        public JSONParse(Dictionary<string, object> jsonObject)
        {
            jsonDict = jsonObject;
        }

        private string rawJSON = string.Empty;

        /// <summary>
        /// 内部对象数据词典
        /// </summary>
        private Dictionary<string, object> jsonDict = new Dictionary<string, object>();

        /// <summary>
        /// 获取原始的JSON反序列化数据词典
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, object> GetJsonDictionary() { return jsonDict; }

        /// <summary>
        /// 获取实例执行属性名称的值
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="objPath">实例json路径</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public object GetObjectValue<T>(string objPath, string key)
            where T:new()
        {
            if (rawJSON == "{}") return null;
            T instance = GetObject<T>(objPath);
            PropertyInfo pi = typeof(T).GetProperty(key);
            if (pi == null)
            {
                return null;
            }
            else
            {
                return pi.GetGetMethod().Invoke(instance, null);
            }
        }

        /// <summary>
        /// 获取JSON数据的根路径上绑定的实例
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <returns>JSON数据的根路径上绑定的实例</returns>
        public T GetObject<T>()
            where T : new()
        {
            return GetObject<T>("");
        }

        /// <summary>
        /// 填充现有实例上的属性数据
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="instance">当前实例</param>
        /// <param name="objPath">json路径</param>
        public void FillObject<T>(T instance, string objPath)
        {
            if (rawJSON != "{}")
            {
                Dictionary<string, object> propDict = GetJsonObjectByPath(objPath, jsonDict);
                DataBindExtension.DataBind<T>(instance, propDict);
                instance.DataBind<T>(propDict);
            }
        }

        /// <summary>
        /// 获取实例
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="objPath">json路径</param>
        /// <returns></returns>
        public T GetObject<T>(string objPath)
            where T:new()
        {
            T instance = new T();
            if (rawJSON != "{}")
            {
                Dictionary<string, object> propDict = GetJsonObjectByPath(objPath, jsonDict);
                instance.DataBind<T>(propDict);
            }
            return instance;
        }

        /// <summary>
        /// 从JSON数据词典获取JSON对象数组
        /// </summary>
        /// <param name="objPath">数组项对象的寻址路径：形如{root:a,other:{a:['yes', 'no']}}为’root','other.a'等</param>
        /// <param name="jsonDict">json原生词典数据</param>
        /// <returns></returns>
        public static object[] GetJsonObjectArray(string objPath, Dictionary<string, object> jsonDict)
        {
            string[] pathRoute = objPath.Split('.');
            object currentObj = null;
            Dictionary<string, object> jsonObject = jsonDict;
            for (int i = 0; i < pathRoute.Length; i++)
            {
                if (!jsonObject.ContainsKey(pathRoute[i]))
                {
                    throw new InvalidOperationException("该路径对象不存在！"
                        + Environment.NewLine
                        + "请求路径：" + pathRoute[i] + Environment.NewLine
                        + "原始数据：" + jsonDict.ToJSON()
                        );
                }
                else
                {
                    currentObj = jsonObject[pathRoute[i]];
                    //不再是对象封装
                    if (currentObj == null || currentObj.GetType() != typeof(Dictionary<string, object>))
                    {
                        break;
                    }
                    else
                    {
                        jsonObject = currentObj as Dictionary<string, object>;
                    }
                }
            }

            //Console.WriteLine();
            //Console.WriteLine(currentObj.GetType());

            if (currentObj == null || currentObj.GetType() != typeof(object[]))
            {
                return new object[0];
            }
            else
            {
                return currentObj as object[];
            }
        }

        /// <summary>
        /// 从JSON数据词典获取JSON对象
        /// </summary>
        /// <param name="objPath">对象的寻址路径：形如{root:a,other:{a:['yes', 'no']}}为’root','other.a'等</param>
        /// <param name="jsonDict">json原生词典数据</param>
        /// <returns></returns>
        public static Dictionary<string, object> GetJsonObjectByPath(string objPath, Dictionary<string, object> jsonDict)
        {
            if (string.IsNullOrEmpty(objPath)) return jsonDict;

            string[] pathRoute = objPath.Split('.');
            object currentObj = null;
            Dictionary<string, object> jsonObject = jsonDict;
            for (int i = 0; i < pathRoute.Length; i++)
            {
                if (!jsonObject.ContainsKey(pathRoute[i]))
                {
                    throw new InvalidOperationException("该路径对象不存在！" 
                        + Environment.NewLine
                        + "请求路径：" + pathRoute[i] + Environment.NewLine
                        + "原始数据：" + jsonDict.ToJSON()
                        );
                }
                else
                {
                    currentObj = jsonObject[pathRoute[i]];
                    //不再是对象封装
                    if (currentObj == null || currentObj.GetType() != typeof(Dictionary<string, object>))
                    {
                        break;
                    }
                    else
                    {
                        jsonObject = currentObj as Dictionary<string, object>;
                    }
                }
            }
            return jsonObject;
        }

        /// <summary>
        /// 获取实例数组
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="objPath">json路径</param>
        /// <returns></returns>
        public T[] GetObjectArray<T>(string objPath)
            where T : new()
        {
            List<T> ts = new List<T>();
            if (rawJSON != "{}")
            {
                object[] oArr = GetJsonObjectArray(objPath, jsonDict);
                for (int i = 0; i < oArr.Length; i++)
                {
                    T instance = new T();
                    instance.DataBind<T>(oArr[i] as Dictionary<string, object>);
                    ts.Add(instance);
                }
            }
            return ts.ToArray();
        }

        internal static string GetDataTypeByExample(object dat)
        {
            string tResult = "string";
            if (dat != null)
            {
                string strDat = dat.ToString().Trim();

                string[] Patterns = new string[] { "^\\d{4}\\-\\d{2}\\-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}$",
                    "^0[1,]\\d+$", //前缀为0的数字转为字符
                    "^\\d{1,9}$", 
                    "^\\d+$",
                    "^true|false$" 
                };

                string[] types = new string[] { "DateTime",
                    "string",
                    "int",
                    "long",
                    "bool" };

                int i = 0;
                foreach (string p in Patterns)
                {
                    if (Regex.IsMatch(strDat, p, RegexOptions.IgnoreCase))
                    {
                        tResult = types[i];
                        break;
                    }
                    i++;
                }
            }
            return tResult;
        }

        /// <summary>
        /// 首字母大写
        /// </summary>
        public static string CapitalUpperCase(string str)
        {
            if (str == null) return string.Empty;
            string[] objWords = str.Split(' ');
            for (int i = 0; i < objWords.Length; i++)
            {
                if (objWords[i] != string.Empty)
                {
                    objWords[i] = objWords[i].ToLower();
                    objWords[i] = objWords[i].Substring(0, 1).ToUpper() + objWords[i].Substring(1);
                }
            }
            return String.Join(" ", objWords);
        }

        /// <summary>
        /// 输出C#实例类
        /// </summary>
        /// <param name="className">实例类名称</param>
        /// <param name="writer">文本输出器</param>
        public void ToEntryClass(string className, TextWriter writer)
        {
            Dictionary<string, Dictionary<string, object>> EntryDict = new Dictionary<string, Dictionary<string, object>>();
            Dictionary<string, object> EmptyEntry = new Dictionary<string, object>();

            writer.WriteLine("public class " + className);
            writer.WriteLine("{");
            foreach (string key in jsonDict.Keys)
            {
                writer.WriteLine();
                if (jsonDict[key].GetType() == typeof(object[]))
                {
                    writer.WriteLine(new string('\t', 1) + "private {0}[] _{0} = new {0}[0];", key);
                    writer.WriteLine(new string('\t', 1) + "public {0}[] {1}", key, key);
                    writer.WriteLine(new string('\t', 1) + "{");
                    writer.WriteLine(new string('\t', 2) + "get { return _" + key + ";}");
                    writer.WriteLine(new string('\t', 2) + "set { _" + key + " = value; }");
                    writer.WriteLine(new string('\t', 1) + "}");
                    if (!EmptyEntry.ContainsKey(key))
                    {
                        EmptyEntry.Add(key, "");
                    }
                    WriteObjectArray(key, jsonDict[key] as object[], writer, EntryDict);
                }
                else
                {
                    writer.WriteLine(new string('\t', 1) + "public {3} {0} {1} get; set; {2}", key,
                        "{", "}",
                        GetDataTypeByExample(jsonDict[key]));
                }
            }

            writer.WriteLine("}");

            writer.WriteLine();

            foreach (string key in EntryDict.Keys)
            {
                Dictionary<string, object> jsDictEntry = EntryDict[key];
                writer.WriteLine("public class " + key);
                writer.WriteLine("{");

                foreach (string item in jsDictEntry.Keys)
                {
                    writer.WriteLine();
                    writer.WriteLine(new string('\t', 1) + "public {3} {0} {1} get; set; {2}",
                        item,
                        "{", "}",
                        GetDataTypeByExample(jsDictEntry[item]));
                }

                writer.WriteLine("}");
            }

            foreach (string emptyKey in EmptyEntry.Keys)
            {
                if (!EntryDict.ContainsKey(emptyKey))
                {
                    writer.WriteLine();
                    writer.WriteLine("public class " + emptyKey);
                    writer.WriteLine("{");
                    writer.WriteLine("}");

                }
            }
        }

        private void WriteObjectArray(string entryKey, object[] objArray, TextWriter writer, Dictionary<string, Dictionary<string, object>> EntryDict)
        {
            foreach (object obj in objArray)
            {
                if (obj != null && obj.GetType() == typeof(Dictionary<string, object>))
                {
                    Dictionary<string, object> jsDic = obj as Dictionary<string, object>;
                    foreach (string key in jsDic.Keys)
                    {
                        if (jsDic[key] != null && jsDic[key].GetType() == typeof(object[]))
                        {
                            //writer.WriteLine(new string('\t', 1) + "private {0}[] _{0} = new {0}[0]", key);
                            //writer.WriteLine(new string('\t', 1) + "public {0}[] {1} get; set; {2}", key, "{", "}");
                            writer.WriteLine(new string('\t', 1) + "private {0}[] _{0} = new {0}[0];", key);
                            writer.WriteLine(new string('\t', 1) + "public {0}[] {1}", key, key);
                            writer.WriteLine(new string('\t', 1) + "{");
                            writer.WriteLine(new string('\t', 2) + "get { return _" + key + ";}");
                            writer.WriteLine(new string('\t', 2) + "set { _" + key + " = value; }");
                            writer.WriteLine(new string('\t', 1) + "}");
                            WriteObjectArray(entryKey, jsDic[key] as object[], writer, EntryDict);
                        }
                        //else
                        //{
                        //    writer.WriteLine(new string('\t', 2) + "public string {0} {1} get; set; {2}", key, "{", "}");
                        //}
                    }

                    if (!EntryDict.ContainsKey(entryKey))
                    {
                        EntryDict.Add(entryKey, jsDic);
                    }
                }
            }
        }

    }
}
